/**
 * @brief SDIO/SD-Card 驱动
 * @author 张勇 / 2020-05-13
 */

#include "share/unios.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "NAND"
#include "share/log4app.h"

#include "string.h"
#include "_mcu_sdmmc.h"

std_err_t mcu_sdmmc_init(mcu_sdmmc_t *sdmmc) {
    if(!sdmmc) return STD_ERR_INVALID_ARG;
    HAL_StatusTypeDef st;
    
    sdmmc->mmc.Instance = SDIO;
    sdmmc->mmc.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;                       // 上升沿 
    sdmmc->mmc.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;                  // 不使用bypass模式，直接用HCLK进行分频得到SDIO_CK 
    sdmmc->mmc.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;           // 空闲时不关闭时钟电源 
    sdmmc->mmc.Init.BusWide = SDIO_BUS_WIDE_1B;                               // 1 位数据线
    sdmmc->mmc.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE; // 关闭硬件流控
    // SD 传输时钟频率最大25MHz, 初始化时的时钟不能大于 400KHZ ?
    // SD 传输时钟分频，由于 HAL 库运行效率低，很容易产生上溢（读SD卡时）/下溢错误（写SD卡时）
    // 使用 1bit 模式时，ClockDiv 改为 0，SDIO时钟频率：48/( ClockDiv + 2 ) = 24M * 1bit = 24Mbps
    // 使用 4bit 模式时，需降低 SDIO 时钟频率，将 ClockDiv 改为 1，SDIO时钟频率：48/( ClockDiv + 2 ) = 16M * 4bit = 64Mbps
    // 使用 8bit 模式时，需降低 SDIO 时钟频率，将 ClockDiv 改为 4，SDIO时钟频率：48/( ClockDiv + 2 ) = 8M * 8bit = 64Mbps
    sdmmc->mmc.Init.ClockDiv = 4;
    st = HAL_MMC_Init(&sdmmc->mmc);
    if(st != HAL_OK) {
        if(sdmmc->mmc.State == HAL_SD_STATE_READY) return STD_ERR_NO_RESOURCES; // 无 SD 卡插入
        return STD_ERR_FAIL; // 其它错误
    }
    
    // 获取 SD/MMC 卡信息
    st = HAL_MMC_GetCardInfo(&sdmmc->mmc, &sdmmc->card);
    if(st != HAL_OK) return STD_ERR_FAIL;

    // 使能 8 位宽总线模式
    st = HAL_MMC_ConfigWideBusOperation(&sdmmc->mmc, SDIO_BUS_WIDE_8B);
    if(st != HAL_OK) return STD_ERR_FAIL;
    
    return STD_ERR_OK;
}

std_err_t mcu_sdmmc_check_card(mcu_sdmmc_t *sdmmc) {
    if(!sdmmc) return STD_ERR_INVALID_ARG;    
    HAL_MMC_CardStateTypeDef cst = HAL_MMC_GetCardState(&sdmmc->mmc);
    if(cst != HAL_MMC_CARD_READY) return STD_ERR_NO_RESOURCES;
    
    HAL_StatusTypeDef st = HAL_MMC_GetCardInfo(&sdmmc->mmc, &sdmmc->card);
    if(st != HAL_OK) return STD_ERR_FAIL;
    return STD_ERR_OK;
}

/**
 * @brief SDMMC 底层驱动，时钟使能，引脚配置
 * @note  此函数会被 HAL_MMC_Init() 调用
 * @param hmmc: SDIO/MMC 句柄
 */
void HAL_MMC_MspInit(MMC_HandleTypeDef *hmmc) {
    // STM32F4xx SDIO 的引脚是固定的:
    // D0/D1/D2/D3/D4/D5/D6/D7/SCK/CMD = PC8/PC9/PC10/PC11/PB8/PB9/PC6/PC7/PC12/PD2
    GPIO_InitTypeDef init = {0};
    init.Mode = GPIO_MODE_AF_PP;
    init.Pull = GPIO_PULLUP;
    init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    init.Alternate = GPIO_AF12_SDIO;
    
    init.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
    HAL_GPIO_Init(GPIOC, &init);      // 初始化 D0/D1/D2/D3/D6/D7/SCK 对应 PC6/7/8/9/10/11/12

    init.Pin = GPIO_PIN_8|GPIO_PIN_9; // 初始化 D4/D5 对应 PB8/PB9
    HAL_GPIO_Init(GPIOB, &init);

    init.Pin = GPIO_PIN_2;
    HAL_GPIO_Init(GPIOD, &init);      // 初始化 CMD 对应 PD2
}

// 获得 SD 卡容量(字节数)
uint64_t mcu_sdmmc_size(mcu_sdmmc_t *sdmmc) {
    return (sdmmc->card.LogBlockNbr) * (sdmmc->card.LogBlockSize);
}

std_err_t mcu_sdmmc_read(mcu_sdmmc_t *sdmmc, uint8_t *pbuf, uint32_t saddr, uint32_t cnt) {
    uint32_t timeout = 1000 * cnt;    // 超时时间(ms)
    HAL_StatusTypeDef sta = HAL_MMC_ReadBlocks(&sdmmc->mmc, pbuf, saddr, cnt, timeout);
    if(sta != HAL_OK) return STD_ERR_FAIL;
    
    // 等待传输操作完成
    unios_wait_ms_while(timeout, timeout, HAL_MMC_GetCardState(&sdmmc->mmc) != HAL_SD_CARD_TRANSFER);
    return timeout ? STD_ERR_OK : STD_ERR_TIMEOUT;
}

std_err_t mcu_sdmmc_write(mcu_sdmmc_t *sdmmc, uint8_t *pbuf, uint32_t saddr, uint32_t cnt) {
    uint32_t timeout = 10000 * cnt;    // 超时时间(ms)
    HAL_StatusTypeDef sta = HAL_MMC_WriteBlocks(&sdmmc->mmc, pbuf, saddr, cnt, timeout);
    if(sta != HAL_OK) {
        return STD_ERR_FAIL;
    }
    
    // 等待传输操作完成
    unios_wait_ms_while(timeout, 100 * cnt, HAL_MMC_GetCardState(&sdmmc->mmc) != HAL_SD_CARD_TRANSFER);
    if(timeout == 0) { 
        return STD_ERR_TIMEOUT; 
    }
    return STD_ERR_OK;
}


DSTATUS ffdisk_sdmmc_init(ffdisk_t *disk) {
    if(disk->initialized) return RES_OK;
    mcu_sdmmc_t *sdmmc = (mcu_sdmmc_t*)disk->dev;
    std_err_t err = mcu_sdmmc_init(sdmmc);
    if(err == STD_ERR_NO_RESOURCES) return RES_NOTRDY;
    if(err) return RES_ERROR;
    
    disk->au_size      = (sdmmc->card.LogBlockNbr <= 7633920) ? (8*1024) : (16*1024); // 若容量大于 4GB，则设置为 16KB，否则 8KB(综合推荐，通用场景)
    disk->sector_size  = sdmmc->card.LogBlockSize;
    disk->sector_count = sdmmc->card.LogBlockNbr;
    disk->sector_base  = 0;
    disk->initialized = true;
    return RES_OK;
}

DSTATUS ffdisk_sdmmc_status(struct _ffdisk *disk) {
    return RES_OK;
}

DRESULT ffdisk_sdmmc_read(struct _ffdisk *disk, BYTE *buff, LBA_t sector, UINT count) {
    if(!disk || !disk->dev || !buff || !count || disk->sector_base + sector + count > disk->sector_count) return RES_PARERR;
    return mcu_sdmmc_read((mcu_sdmmc_t*)disk->dev, buff, sector, count) ? RES_ERROR : RES_OK;
}

DRESULT ffdisk_sdmmc_write(struct _ffdisk *disk, const BYTE *buff, LBA_t sector, UINT count) {
    if(!disk || !disk->dev || !buff || !count || disk->sector_base + sector + count > disk->sector_count) return RES_PARERR;
    return mcu_sdmmc_write((mcu_sdmmc_t*)disk->dev, (uint8_t *)buff, sector, count) ? RES_ERROR : RES_OK;
}

DRESULT ffdisk_sdmmc_ioctl(struct _ffdisk *disk, BYTE cmd, void *buff) {
    if(!disk || !disk->dev) return RES_PARERR;
    
    switch (cmd) {
        case GET_BLOCK_SIZE:
            if(buff) *(WORD*)buff = disk->au_size;
            return RES_OK;
        
        case GET_SECTOR_SIZE:
            if(buff) *(WORD*)buff = disk->sector_size;
            return RES_OK;

        case GET_SECTOR_COUNT:
            if(buff) *(LBA_t*)buff = disk->sector_count;
            return RES_OK;

        case CTRL_SYNC:
            // 不使用文件读写缓冲区，所以无需同步操作，直接返回 OK
            return RES_OK;

        default: return RES_PARERR;
    }
}
